//////////////////////////////////////////////////////////////////////////////
// Product: QEP/C++ platform-independent public interface
// Last Updated for Version: 3.4.01
// Date of the Last Update:  Sep 25, 2007
//
//                    Q u a n t u m     L e a P s
//                    ---------------------------
//                    innovating embedded systems
//
// Copyright (C) 2002-2007 Quantum Leaps, LLC. All rights reserved.
//
// This software may be distributed and modified under the terms of the GNU
// General Public License version 2 (GPL) as published by the Free Software
// Foundation and appearing in the file GPL.TXT included in the packaging of
// this file. Please note that GPL Section 2[b] requires that all works based
// on this software must also be made publicly available under the terms of
// the GPL ("Copyleft").
//
// Alternatively, this software may be distributed and modified under the
// terms of Quantum Leaps commercial licenses, which expressly supersede
// the GPL and are specifically designed for licensees interested in
// retaining the proprietary status of their code.
//
// Contact information:
// Quantum Leaps Web site:  http://www.quantum-leaps.com
// e-mail:                  info@quantum-leaps.com
//////////////////////////////////////////////////////////////////////////////
#ifndef qep_h
#define qep_h

/// \ingroup qep qf qk
/// \file qep.h
/// \brief QEP/C++ platform-independent public interface.
/// This header file must be included directly or indirectly
/// in all modules (*.cpp files) that use QEP/C++.

#ifndef Q_ROM
    /// \brief Macro to specify compiler-specific directive for placing a
    /// constant object in ROM.
    ///
    /// Many compilers for Harvard-architecture MCUs provide non-stanard
    /// extensions to support placement of objects in different memories.
    /// In order to conserve the precious RAM, QP uses the Q_ROM macro for
    /// all constant objects that can be allocated in ROM.
    ///
    /// To override the following empty definition, you need to define the
    /// Q_ROM macro in the qep_port.h header file. Some examples of valid
    /// Q_ROM macro definitions are: __code (IAR 8051 compiler), code (Keil
    /// Cx51 compiler), PROGMEM (gcc for AVR), __flash (IAR for AVR).
    #define Q_ROM
#endif
#ifndef Q_ROM_VAR            // if NOT defined, provide the default definition

    /// \brief Macro to specify compiler-specific directive for accessing a
    /// constant object in ROM.
    ///
    /// Many compilers for MCUs provide different size pointers for
    /// accessing objects in various memories. Constant objects allocated
    /// in ROM (see #Q_ROM macro) often mandate the use of specific-size
    /// pointers (e.g., far pointers) to get access to ROM objects. The
    /// macro Q_ROM_VAR specifies the kind of the pointer to be used to access
    /// the ROM objects.
    ///
    /// To override the following empty definition, you need to define the
    /// Q_ROM_VAR macro in the qep_port.h header file. An example of valid
    /// Q_ROM_VAR macro definition is: __far (Freescale HC(S)08 compiler).
    #define Q_ROM_VAR
#endif

//////////////////////////////////////////////////////////////////////////////
/// \brief Provides miscellaneous QEP services.
class QEP {
public:
    /// \brief get the current QEP version number string
    ///
    /// \return version of the QEP as a constant 6-character string of the
    /// form x.y.zz, where x is a 1-digit major version number, y is a
    /// 1-digit minor version number, and zz is a 2-digit release number.
    static char const Q_ROM * Q_ROM_VAR getVersion(void);
};

//////////////////////////////////////////////////////////////////////////////
/// helper macro to calculate static dimension of a 1-dim array \a array_
#define Q_DIM(array_) (sizeof(array_) / sizeof(array_[0]))

#ifndef QEP_SIGNAL_SIZE
    #define QEP_SIGNAL_SIZE 1
#endif
#if (QEP_SIGNAL_SIZE == 1)
    //////////////////////////////////////////////////////////////////////////
    /// \brief QSignal represents the signal of an event.
    ///
    /// The relationship between an event and a signal is as follows. A signal
    /// in UML is the specification of an asynchronous stimulus that triggers
    /// reactions [<A HREF="http://www.omg.org/docs/ptc/03-08-02.pdf">UML
    /// document ptc/03-08-02</A>], and as such is an essential part of an
    /// event. (The signal conveys the type of the occurrence-what happened?)
    /// However, an event can also contain additional quantitative information
    /// about the occurrence in form of event parameters. Please refer to the
    /// document
    /// <A HREF="http://www.quantum-leaps.com/devzone/Recipe_IntroHSM.pdf">
    /// Brief Introduction to UML State Machines</A>) for more information
    /// about state machine concepts.
    typedef uint8_t QSignal;
#elif (QEP_SIGNAL_SIZE == 2)
    typedef uint16_t QSignal;
#elif (QEP_SIGNAL_SIZE == 4)
    typedef uint32_t QSignal;
#else
    #error "QEP_SIGNAL_SIZE defined incorrectly, expected 1, 2, or 4"
#endif

//////////////////////////////////////////////////////////////////////////////
/// \brief QEvent base class.
///
/// QEvent represents events without parameters and serves as the base class
/// for derivation of events with parameters.
///
/// \note All data members of the QEvent class must remain public to keep it
/// an AGGREGATE. Therefore, the attribute QEvent::attrQF__ cannot be
/// declared private.
///
/// The following example illustrates how to add an event parameter by
/// inheriting from the QEvent class.
/// \include qep_qevent.cpp
struct QEvent {
    QSignal sig;                             ///< signal of the event instance
    uint8_t dynamic_;///< dynamic attributes of the event (0 for static event)
};

//////////////////////////////////////////////////////////////////////////////
/// \brief QEP reserved signals.
enum QReservedSignals {
    Q_ENTRY_SIG = 1,                             ///< signal for entry actions
    Q_EXIT_SIG,                                   ///< signal for exit actions
    Q_INIT_SIG,                     ///< signal for nested initial transitions
    Q_USER_SIG                              ///< signal to offset user signals
};

//////////////////////////////////////////////////////////////////////////////
class QFsm;                                             // forward declaration

                            /// \brief the signature of state handler function
typedef void (*QState)(QFsm *me, QEvent const *e);

//////////////////////////////////////////////////////////////////////////////
/// \brief Finite State Machine base class
///
/// QFsm represents a traditional non-hierarchical Finite State Machine (FSM)
/// without state hierarchy, but with entry/exit actions.
///
/// QFsm is also a base structure for the ::QHsm class.
///
/// \note QFsm is not intended to be instantiated directly, but rather serves
/// as the base class for derivation of state machines in the application
/// code.
///
/// The following example illustrates how to derive a state machine class
/// from QFsm.
/// \include qep_qfsm.cpp
class QFsm {
protected:
    QState state_;                                   ///< current active state

public:

    /// \brief Performs the second step of FSM initialization by triggering
    /// the top-most initial transition.
    ///
    /// \param e constant pointer ::QEvent or a class derived from ::QEvent
    /// \note Must be called only ONCE before QFsm::dispatch()
    ///
    /// The following example illustrates how to initialize a FSM, and
    /// dispatch events to it:
    /// \include qep_qfsm_use.cpp
    virtual void init(QEvent const *e = (QEvent const *)0);

    /// \brief Dispatches an event to a FSM
    ///
    /// Processes one event at a time in Run-to-Completion fashion. The
    /// argument \a me_ is the pointer the state machine class derived from
    /// ::QFsm. The argument \a e_ is a constant pointer the ::QEvent or a
    /// class derived from ::QEvent.
    ///
    /// \note Must be called after QFsm::init().
    ///
    /// \sa example for QFsm::init()
    virtual void dispatch(QEvent const *e);

protected:

    /// \brief Protected constructor of a FSM.
    ///
    /// Performs the first step of FSM initialization by assigning the
    /// initial pseudostate to the currently active state of the state
    /// machine.
    ///
    /// \note The constructor is protected to prevent direct instantiating
    /// of QFsm objects. This class is intended for subclassing only.
    ///
    /// \sa The ::QFsm example illustrates how to use the QHsm constructor
    /// in the constructor initializer list of the derived state machines.
    QFsm(QState initial) : state_(initial) {}

    /// \brief Protected virtual destructor of a FSM.
    virtual ~QFsm();

    /// \brief Returns current active state of a FSM.
    ///
    /// \note this is a protected function to be used only inside state
    /// handler functions.
    QState getState(void) const {
        return state_;
    }
};

/// \brief Designates a target for an initial or regular transition.
///
/// Q_TRAN() can be used both in the FSMs and HSMs:
///
/// \include qep_qtran.cpp
#define Q_TRAN(target_)     (me->state_ = (QState)(target_))


//////////////////////////////////////////////////////////////////////////////
class QHsm;                                             // forward declaration

                    /// \brief the signature of state handler function for HSM
typedef QState (*QHsmState)(QHsm *me, QEvent const *e);

     /// \brief a name for the return type from the HSM state handler function
typedef QState QSTATE;

//////////////////////////////////////////////////////////////////////////////
/// \brief Hierarchical State Machine base class
///
/// QHsm represents a Hierarchical Finite State Machine (HSM). QHsm derives
/// from the ::QFsm class and extends the capabilities of a basic FSM
/// with state hierarchy.
///
/// \note QHsm is not intended to be instantiated directly, but rather serves
/// as the base structure for derivation of state machines in the application
/// code.
///
/// The following example illustrates how to derive a state machine class
/// from QHsm.
/// \include qep_qhsm.cpp
class QHsm {
protected:
    QState state_;                                   ///< current active state

public:
    virtual void init(QEvent const *e = (QEvent const *)0);
    virtual void dispatch(QEvent const *e);

    /// \brief Tests if a given state is part of the current active state
    /// configuratioin
    ///
    /// \param state is a pointer to the state handler function, e.g.,
    /// &QCalc::on.
    uint8_t isIn(QHsmState state);

protected:

    /// \brief Protected constructor of a HSM.
    ///
    /// Performs the first step of HSM initialization by assigning the
    /// initial pseudostate to the currently active state of the state
    /// machine.
    ///
    /// \note The constructor is protected to prevent direct instantiating
    /// of QHsm objects. This class is intended for subclassing only.
    ///
    /// \sa The ::QHsm example illustrates how to use the QHsm constructor
    /// in the constructor initializer list of the derived state machines.
    /// \sa QFsm::QFsm()
    QHsm(QState initial) : state_(initial) {}

    /// \brief the top-state.
    ///
    /// QHsm::top() is the ultimate root of state hierarchy in all HSMs
    /// derived from ::QHsm. This state handler always returns (QSTATE)0,
    /// which means that it "handles" all events.
    ///
    /// \sa Example of the QCalc::on() state handler.
    static QSTATE top(QHsm *me, QEvent const *e);

    /// \return the current active state of the HSM
    QHsmState getState(void) const {
        return (QHsmState)state_;
    }
};

//////////////////////////////////////////////////////////////////////////////
//  DEPRECATED  DEPRECATED  DEPRECATED  DEPRECATED  DEPRECATED  DEPRECATED

// If QP compatibility level not defined or the level is lower than v3.4
#if (!defined(QP_COMP_LEVEL) || (QP_COMP_LEVEL < 34))

/// \brief Designates a target for an initial transition.
///
/// \note DEPRECATED
/// \sa Q_TRAN()
#define Q_INIT(target_)         Q_TRAN(target_)


/// \brief Dynamic state transition.
///
/// \note DEPRECATED
/// \sa Q_TRAN()
#define Q_TRAN_DYN(target_)     Q_TRAN(target_)

/// \brief Optimized static transition.
///
/// \note DEPRECATED
/// \sa Q_TRAN()
#define Q_TRAN_STA(target_)     Q_TRAN(target_)

#endif                                                   // QP_COMP_LEVEL < 34

#endif                                                                // qep_h
